package cn.line5.seam.db;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.line5.seam.cache.DBConfigCache;
import cn.line5.seam.contexts.JdbcContext;
import cn.line5.seam.exception.DBException;
import cn.line5.seam.exception.SeamException;
import cn.line5.seam.util.ReflectUtil;

/**
 * <h1>查询实现类</h1> <br/>
 * 
 * @author Liu,Xiaojun
 * @since JDK1.6
 */
public class QueryImpl extends AbstractQuery implements Query {

	private static final Logger log = LoggerFactory.getLogger(QueryImpl.class);
	private ResultSet rs;
	private Integer pageRows = -1;
	private Integer currentPage = -1;

	/**
	 * 构造方法
	 * 
	 * @param queryString
	 *            SQL语句
	 */
	public QueryImpl(String queryString) {
		super(queryString);
	}

	/**
	 * 构造方法
	 * 
	 * @param queryString
	 *            SQL语句
	 * @param obj
	 *            条件对象，将根据SQL对应的:xxx调用条件对象对应的属性值
	 */
	public QueryImpl(String queryString, Object obj) {
		super(queryString, obj);
	}

	@Override
	/**
	 * 执行查询单条数据
	 * 
	 * @return 装载数据的Map对象
	 * @throws DBException
	 *             SQL查询异常
	 */
	public Map<String, Object> single() throws DBException {
		List<Map<String, Object>> list = list();
		return (list != null && list.size() > 0) ? list.get(0) : null;
	}

	@Override
	/**
	 * 执行查询单条数据
	 * 
	 * @param <T>
	 *            装载数据类泛型
	 * @param clazz
	 *            数据类（VO）的class
	 * @return 装载传入数据类（VO）的数据
	 * @throws DBException
	 *             SQL查询异常
	 */
	public <T> T single(Class<T> clazz) throws DBException {
		List<Map<String, Object>> list = list();
		if (list != null && list.size() > 0) {
			Map<String, Object> m = list.get(0);
			Field[] fields = clazz.getDeclaredFields();
			try {
				T obj = clazz.newInstance();
				Iterator<Entry<String, Object>> it = m.entrySet().iterator();
				while (it.hasNext()) {
					Entry<String, Object> e = it.next();
					for (Field field : fields) {
						if (field.getName().equalsIgnoreCase(e.getKey())) {
							ReflectUtil.setFieldValue(field.getName(), obj,
									e.getValue());
							break;
						}
					}
				}
				return obj;
			} catch (InstantiationException e) {
				throw new SeamException("The class '" + clazz.getName()
						+ "' instance error.", e);
			} catch (IllegalAccessException e) {
				throw new SeamException("The class '" + clazz.getName()
						+ "' instance error.", e);
			}
		}
		return null;
	}

	@Override
	/**
	 * 执行查询
	 * 
	 * @return 装载数据的Map对象列表
	 * @throws DBException
	 *             SQL查询异常
	 */
	public List<Map<String, Object>> list() throws DBException {
		formatSql();
		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
		if (DBConfigCache.showsql)
			log.info("LSEAM query sql: " + sql);
		try {
			conn = JdbcContext.getConnection();
			ps = conn.prepareStatement(sql);
			setParameter();
			rs = ps.executeQuery();
			list = convert();
		} catch (SQLException e) {
			throw new DBException("Error in query, the SQL is:", sql, e);
		} finally {
			releaseResource();
		}
		return list;
	}

	@Override
	/**
	 * 执行查询
	 * 
	 * @param <T>
	 *            装载数据类泛型
	 * @param clazz
	 *            数据类（VO）的class
	 * @return 装载传入数据类（VO）的数据列表
	 * @throws DBException
	 *             SQL查询异常
	 */
	public <T> List<T> list(Class<T> clazz) throws DBException {
		return covertList(list(), clazz);
	}

	@Override
	/**
	 * 查询记录数，执行含有count()的SQL
	 * 
	 * @return 记录数
	 * @throws DBException
	 *             SQL查询异常
	 */
	public Integer count() throws DBException {
		Integer result = 0;
		formatSql();
		if (DBConfigCache.showsql)
			log.info("LSEAM query sql: " + sql);
		try {
			conn = JdbcContext.getConnection();
			ps = conn.prepareStatement(sql);
			setParameter();
			rs = ps.executeQuery();
			if (rs.next()) {
				result = rs.getInt(1);
			}
		} catch (SQLException e) {
			throw new DBException("Error in query, the SQL is:", sql, e);
		} finally {
			releaseResource();
		}
		return result;
	}

	@Override
	/**
	 * 执行分页查询
	 * 
	 * @param <T>
	 *            装载数据类泛型
	 * @param clazz
	 *            数据类（VO）的class
	 * @return 装载传入数据类（VO）的分页数据对象
	 * @throws DBException
	 *             SQL查询异常
	 */
	public <T> PageResult<T> pageList(Class<T> clazz) throws DBException {
		PageResult<T> covertPage = new PageResult<T>();
		PageResult<Map<String, Object>> page = pageList();
		covertPage.setCurrentPage(page.getCurrentPage());
		covertPage.setFirstRow(page.getFirstRow());
		covertPage.setLastRow(page.getLastRow());
		covertPage.setTotalPages(page.getTotalPages());
		covertPage.setTotalRows(page.getTotalRows());
		covertPage.setPageRows(page.getPageRows());
		covertPage.setResultList(covertList(page.getResultList(), clazz));
		return covertPage;
	}

	@Override
	/**
	 * 执行分页查询
	 * 
	 * @return 装载数据的Map对象列表
	 * @throws DBException
	 *             SQL查询异常
	 */
	public PageResult<Map<String, Object>> pageList() throws DBException {
		if (this.pageRows <= 0)
			throw new DBException(
					"Must set the pageRows and keep it greater than 0.");
		if (this.currentPage <= 0)
			throw new DBException(
					"Must set the currentPage and keep it greater than 0.");
		PageResult<Map<String, Object>> page = new PageResult<Map<String, Object>>();
		page.setCurrentPage(this.currentPage);
		page.setPageRows(this.pageRows);
		Integer totalRows = findCount();
		if (totalRows > 0) {
			page.setTotalRows(totalRows);
			page.setTotalPages(totalRows % pageRows > 0 ? totalRows / pageRows
					+ 1 : totalRows / pageRows);
			if (page.getCurrentPage() > page.getTotalPages())
				page.setCurrentPage(page.getTotalPages());
			page.setFirstRow((page.getCurrentPage() - 1) * this.pageRows + 1);
			List<Map<String, Object>> resultList = findList(page.getFirstRow() - 1);
			page.setLastRow(page.getFirstRow() + resultList.size() - 1);
			page.setResultList(resultList);
		}
		return page;
	}

	@Override
	/**
	 * 关闭资源
	 */
	public void releaseResource() throws DBException {
		try {
			if (rs != null)
				rs.close();
			if (ps != null)
				ps.close();
		} catch (SQLException e) {
			throw new DBException("Release the DB Resource is Error.", e);
		}
	}

	@Override
	/**
	 * 设置每页行数
	 */
	public Query setPageRows(Integer pageRows) {
		this.pageRows = pageRows;
		return this;
	}

	@Override
	/**
	 * 设置当前页数
	 */
	public Query setCurrentPage(Integer currentPage) {
		this.currentPage = currentPage;
		return this;
	}

	/**
	 * 将数据装载到Map中
	 */
	private List<Map<String, Object>> convert() throws DBException {
		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
		try {
			ResultSetMetaData rsmd = rs.getMetaData();
			int numberOfColumns = rsmd.getColumnCount();
			Map<String, Object> record;
			while (rs.next()) {
				record = new HashMap<String, Object>();
				for (int i = 1; i <= numberOfColumns; i++) {
					record.put(rsmd.getColumnLabel(i).toLowerCase(),
							rs.getObject(i));
				}
				list.add(record);
			}
		} catch (SQLException e) {
			throw new DBException("Error in query, the SQL is:", sql, e);
		}
		return list;
	}

	/**
	 * 将Map数据对象组装成T的数据对象
	 */
	private <T> List<T> covertList(List<Map<String, Object>> l, Class<T> clazz) {
		List<T> list = new ArrayList<T>();
		if (l != null && l.size() > 0) {
			Field[] fields = clazz.getDeclaredFields();
			for (Map<String, Object> m : l) {
				try {
					T obj = clazz.newInstance();
					Iterator<Entry<String, Object>> it = m.entrySet()
							.iterator();
					while (it.hasNext()) {
						Entry<String, Object> e = it.next();
						for (Field field : fields) {
							if (field.getName().equalsIgnoreCase(e.getKey())) {
								ReflectUtil.setFieldValue(field.getName(), obj,
										e.getValue());
								break;
							}
						}
					}
					list.add(obj);
				} catch (InstantiationException e) {
					throw new SeamException("The class '" + clazz.getName()
							+ "' instance error.", e);
				} catch (IllegalAccessException e) {
					throw new SeamException("The class '" + clazz.getName()
							+ "' instance error.", e);
				}
			}
		}
		return list;
	}

	/**
	 * 根据SQL重新组装查询数据行数的SQL，并进行查询
	 */
	private Integer findCount() throws DBException {
		Integer result = 0;
		formatSql();
		Dialect dialect = DialectFactory.createDialect();
		if(dialect == null)
			throw new DBException("Can't find the dialect by named:" + DBConfigCache.databaseType);
		String sql = dialect.getCountSql(this.sql);
		if (DBConfigCache.showsql)
			log.info("LSEAM query sql: " + sql);
		try {
			conn = JdbcContext.getConnection();
			ps = conn.prepareStatement(sql);
			setParameter();
			rs = ps.executeQuery();
			if (rs.next()) {
				result = rs.getInt(1);
			}
		} catch (SQLException e) {
			throw new DBException("Error in query, the SQL is:", sql, e);
		} finally {
			releaseResource();
		}
		return result;
	}

	/**
	 * 执行分页查询
	 */
	private List<Map<String, Object>> findList(Integer firstRow)
			throws DBException {
		Dialect dialect = DialectFactory.createDialect();
		if(dialect == null)
			throw new DBException("Can't find the dialect by named:" + DBConfigCache.databaseType);
		String sql = dialect.getLimitSql(this.sql);
		Integer param1 = dialect.getPageFirstParam(firstRow, this.pageRows);
		Integer param2 = dialect.getPageSecondParam(firstRow, this.pageRows);
		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
		if (DBConfigCache.showsql)
			log.info("LSEAM query sql: " + sql);
		try {
			conn = JdbcContext.getConnection();
			ps = conn.prepareStatement(sql);
			setParameter();
			ps.setInt(this.parameterList.size() + 1, param1);
			ps.setInt(this.parameterList.size() + 2, param2);
			rs = ps.executeQuery();
			list = convert();
		} catch (SQLException e) {
			throw new DBException("Error in query, the SQL is:", sql, e);
		} finally {
			releaseResource();
		}
		return list;
	}
}